<!doctype html>
<html>
<head>
  <meta charset="utf-8"/>
  <meta http-equiv="X-UA-Compatible" content="IE=7; IE=EmulateIE9; IE=10" />
  <title>HERE Maps API Example: KML Placemark Generator</title>
  <!-- Set up constants such as APP ID and token -->
  <script type="text/javascript" src="../libs/hereAppIdAndToken.js"></script>
  <!-- Bootstrap jQuery Library -->
  <script type="text/javascript" src="../libs/jQl.min.js"></script>

  <!-- Asynchronously the HERE Maps API for JavaScript -->
  <script type="text/javascript" src="../libs/hereAsyncLoader.js"
    id="HereMapsLoaderScript"
    data-params="maps,datarendering"
    data-map-container="mapContainer"
    data-callback="afterHereMapLoad"
    data-libs="kml-generator" 
    data-parent="demos/generate-kml/">
  </script>
  <link href='../css/prism.css' rel='stylesheet'>
  <script type='text/javascript' src='../libs/prism.js' ></script>
  <link rel="icon" href="http://here.com/favicon.ico"/>
  <style type="text/css">
    h2, h3 {
      color: blue;
    }
    h3 {
      font-size: 1.5em;
    }
    label {
      display: block;
      margin: 2px 0;
    }
    ol li {
      display: block;
    }
    #mapContainer {
      width: 540px;
      height: 334px;
    }
    #kmlData{
      position:absolute;
      width: 540px;
      top: 30px;
      left: 600px;
      color: #666;
      background-color: #f5f2f0;
    }
    .formContainer {
      width: 500px;
      line-height: 1.5em;
    }
    .underline {
      text-decoration: underline;
    }
  </style>
</head>
<body>
  <h1>KML Placemark Generator</h1>
<div class="formContainer">
  <p>This example is able to covert an arbitrary file format to KML. Three sample files are provided:</p>
<ul>
<li>
    <strong>Proprietary format</strong> of data from the <a href="http://www.rightmove.co.uk/ps/pdf/guides/V3TestFile.blm"> Right Move Estate Agent</a>
     website.<br/>
     It uses these <span id="rightMoveSetUp" class="underline">Settings</span>.
     The result can be seen in the <a href="./load-kml-estate-agents.html">Estate Agents Example</a>
</li>
<li>
    A <strong>tab delimited text file</strong> of <a href="./data/chinese-cities.csv">Chinese Cities</a><br/>
    It uses these <span id="chinaSetUp" class="underline">Format settings</span>.
    The result can be seen here: <a href="./load-kml-cities-in-china.html">Chinese Cities</a>
</li>
<li>
  An <strong>HTML table</strong> from Wikipedia showing the <a href="./data/Largest_cities_of_Finland.html">Population of Cities in Finland</a>
  uses this <span id="finlandSetUp" class="underline">format</span>.
  The result can be seen here: <a href="./load-kml-cities-in-finland.html">Cities in Finland</a>
</li>
</ul>

  <h2>1. Load Data</h2>
  <label for="dataInput">Data Input:</label><br /><textarea id="dataInput" rows="10" cols="70" ></textarea>
    <br />
  <hr/>

<br />
  <h2>2. Define Format</h2>
  <hr/>
    <label for="headerStart">Start of Header Indicator</label>
    <input id="headerStart" type="text" value="" />
    <label for="dataStart">Start of Data Indicator</label> <input id="dataStart" type="text" value="\n" /><br />
    <label for="lineSep">Record Separator:</label> <input id="lineSep" type="text" size="10" value="\n" />
    <label for="fieldSep">Field Separator:</label> <input id="fieldSep" type="text" size="10" value="\t" />

    <input type="button" id="csvSetUp" value="CSV file" />
    <input type="button" id="tabSetUp" value="Tab" />

    <br />
    <hr />
    <label for="idField">ID Field:</label> <input id="idField" type= "text" size="50" value="" />
    <label for="styleURLFields">Style URL Field:</label> <input id="styleURLFields" type="text" size="50" value="" /><br />
    <label for="descriptionFields">Description Fields:</label><input id="descriptionFields" type="text" size="100" value="" /><br />
    <label for="nameFields">Name Fields:</label> <input id="nameFields" type="text" size="100" value= "" /><br />
    <hr />
    <label for="addressAttempt1">Address Attempts:</label><br />
    <ol>
      <li><input id="addressAttempt1" type="text" size="100" value="" /></li>
      <li><input id="addressAttempt2" type="text" size="100" value="" /></li>
      <li><input id="addressAttempt3" type="text" size="100" value="" /></li>
      <li><input id="addressAttempt4" type="text" size="100" value="" /></li>
    </ol>
    Alternatively: <label for="rGeoAttemptLng">Latitiude/Longitude (if known):</label><br />
    <input id="rGeoAttemptLat" type="text" size="10" value="" />
    <input id="rGeoAttemptLng" type="text" size="10" value="" /><br />
    <h2>3. Run Geocoder: <input id="geocodeData" type="button" value="Generate KML" /></h2>
</div>
  <h3 id="ticker" style="height: 2.0em; font-size: 2em; color: blue;"></h3>
 <div id="mapContainer"></div>
 <div id="kmlData"></div>
 <div id="src"><br/><p>Code:</p></div>
 
<script id="example-code" data-categories="kml" type="text/javascript" >
//<![CDATA[
var map,
  kmlGenerator,
  infoBubbles,
  addressFields,
  addressingAttempt,
  currentRecord,
  address,
  id,
  description,
  name,
  styleURL,
  rows,
  fieldSep,
  headers,
  descriptionFields,
  nameFields,
  idFields,
  styleURLFields,
  rGeoFields,
  onSearchComplete,
  headerStart,
  dataStart,
  lineSep,
  rawFields;


function addKMLGenerator(map) {
  kmlGenerator = new KMLGenerator(document.getElementById("kmlData"), {
    datasource : '$data',
    id : 'id',
    description: 'description',
    name: 'name',
    address: 'address',
    styleURL: 'styleURL'
  });
  map.addComponent(kmlGenerator);
}



// Standard Library for spliting up Comma Delimited Texts.
String.prototype.splitCSV = function (sep) {
  for (var foo = this.split(sep = sep || ","), x = foo.length - 1, tl; x >= 0; x--) {
    if (foo[x].replace(/"\s+$/, '"').charAt(foo[x].length - 1) == '"') {
      if ((tl = foo[x].replace(/^\s+"/, '"')).length > 1 && tl.charAt(0) == '"') {
        foo[x] = foo[x].replace(/^\s*"|"\s*$/g, '').replace(/""/g, '"');
      } else if (x) {
        foo.splice(x - 1, 2, [foo[x - 1], foo[x]].join(sep));
      } else foo = foo.shift().split(sep).concat(foo);
    } else foo[x].replace(/""/g, '"');
  }
  return foo;
};



function rightMoveSetUp() {
  $('#rightMoveSetUp').click(function () {
    $('#headerStart').val('#DEFINITION#\\n');
    $('#dataStart').val('#DATA#\\n');
    $('#lineSep').val('|\\n\\n');
    $('#fieldSep').val('^');
    $('#idField').val('AGENT_REF');
    $('#styleURLFields').val('BEDROOMS');
    $('#descriptionFields').val('DESCRIPTION');

    $('#nameFields').val('ADDRESS_1^ADDRESS_2^ADDRESS_3');
    $('#addressAttempt1').val('ADDRESS_1^ADDRESS_2^ADDRESS_3^TOWN^POSTCODE1^POSTCODE2');
    $('#addressAttempt2').val('ADDRESS_2^ADDRESS_3^TOWN^POSTCODE1^POSTCODE2');
    $('#addressAttempt3').val('POSTCODE1^POSTCODE2');
    $('#addressAttempt4').val('TOWN');
    $('#rGeoAttemptLat').val('');
    $('#rGeoAttemptLng').val('');
  });
}
//
//
//
function finlandSetUp() {
  $('#finlandSetUp').click(function () {
    $('#headerStart').val('');
    $('#dataStart').val('\\n');
    $('#lineSep').val('\\n');
    $('#fieldSep').val('\\t');
    $('#idField').val('Rank');
    $('#styleURLFields').val('');
    $('#descriptionFields').val('<h2>\\tCore city\\t<br/>\\tRegion \\t</h2>pop: \\tUrban population');
    $('#nameFields').val('Core city');
    $('#addressAttempt1').val('Core city\\tRegion');
    $('#addressAttempt2').val('Core city');
    $('#addressAttempt3').val('');
    $('#addressAttempt4').val('');
    $('#rGeoAttemptLat').val('');
    $('#rGeoAttemptLng').val('');
  });
}
//
//
//
function chinaSetUp() {
  $('#chinaSetUp').click(function () {
    $('#headerStart').val('');
    $('#dataStart').val('\\n');
    $('#lineSep').val('\\n');
    $('#fieldSep').val('\\t');
    $('#idField').val('Rank');
    $('#styleURLFields').val('');
    $('#descriptionFields').val('<h2>\\tChinese Name\\t</h2>\\t<br/>pop:\\tBuilt-up area\\t<br/>urban:\\tUrban area');
    $('#nameFields').val('English Name');
    $('#addressAttempt1').val('Chinese Name');
    $('#addressAttempt2').val('Chinese Name\\tEnglish Name');
    $('#addressAttempt3').val('English Name');
    $('#addressAttempt4').val('');
    $('#rGeoAttemptLat').val('');
    $('#rGeoAttemptLng').val('');
  });
}


function tabSetUp() {
  $('#tabSetUp').click(function () {
    $('#headerStart').val('');
    $('#dataStart').val('\\n');
    $('#lineSep').val('\\n');
    $('#fieldSep').val('\\t');
    $('#idField').val('');
    $('#styleURLFields').val('');
    $('#descriptionFields').val('');
    $('#nameFields').val('');
    $('#addressAttempt1').val('');
    $('#addressAttempt2').val('');
    $('#addressAttempt3').val('');
    $('#addressAttempt4').val('');
    $('#rGeoAttemptLat').val('');
    $('#rGeoAttemptLng').val('');
  });
}

//
//
//
function csvSetUp() {
  $('#csvSetUp').click(function () {
    $('#headerStart').val('');
    $('#dataStart').val('\\n');
    $('#lineSep').val('\\n');
    $('#fieldSep').val(',');
    $('#idField').val('');
    $('#styleURLFields').val('');
    $('#descriptionFields').val('');
    $('#nameFields').val('');
    $('#addressAttempt1').val('');
    $('#addressAttempt2').val('');
    $('#addressAttempt3').val('');
    $('#addressAttempt4').val('');
    $('#rGeoAttemptLat').val('');
    $('#rGeoAttemptLng').val('');
  });
}


/* Adds a new marker to the map, with additional functionality based on the edit box data. */
function addMarker(markerData) {

  if (markerData.title === undefined) {
    markerData.title = '';
  }
  var TOUCH = nokia.maps.dom.Page.browser.touch,
    CLICK = TOUCH ? 'tap' : 'click',
    marker = new nokia.maps.map.StandardMarker(markerData.coords, {
      text: markerData.title, //small title
      $data: markerData
    });

  // When the marker is clicked,show Infobubble.
  marker.addListener(CLICK, function (evt) {
    if (evt.target.$data.description !== '') {
      infoBubbles.openBubble(evt.target.$data.description, evt.target.coordinate);
    } else {
      infoBubbles.removeBubble();
    }
  }, false);

  map.objects.add(marker);
}


function getFieldsFromDefinition(definition, headerFields, dataRecord) {
  var result = '',
    defFieldCount,
    headerFieldCount,
    head,
    def;

  for (defFieldCount = 0; defFieldCount < definition.length; defFieldCount += 1) {
    for (headerFieldCount = 0; headerFieldCount < headerFields.length; headerFieldCount += 1) {
      head =  headerFields[headerFieldCount].trim();
      def = definition[defFieldCount].trim();
      if (head === def) {
        if (headerFieldCount <= dataRecord.length) {
          result = result + dataRecord[headerFieldCount] + ' ';
        }
        break;
      }
      if (headerFieldCount === headerFields.length - 1) {
        if (headerFieldCount <= dataRecord.length) {
          result = result + definition[defFieldCount];
        }
      }
    }

  }
  result.trim();
  return result;
}


// Obtains the Longitude and Latitude of the next record
// Based upon the data in the chosen fields of that recod.
function doNextGeoCode() {

  if (currentRecord < rows.length) {

    var row = rows[currentRecord].splitCSV(fieldSep),
      lat,
      lng;

    address = getFieldsFromDefinition(addressFields[addressingAttempt], headers, row);
    description = getFieldsFromDefinition(descriptionFields, headers, row);
    name = getFieldsFromDefinition(nameFields, headers, row);
    id = getFieldsFromDefinition(idFields, headers, row);
    styleURL = getFieldsFromDefinition(styleURLFields, headers, row);



    if (rGeoFields !== '') {

      lat = getFieldsFromDefinition(rGeoFields.latitude, headers, row);
      lng = getFieldsFromDefinition(rGeoFields.longitude, headers, row);
      if (lat !== '' && lng !== '') {
        nokia.places.search.manager.reverseGeoCode({
          latitude: lat,
          longitude: lng,
          onComplete: onSearchComplete
        });
      } else {
        addressingAttempt += 1;
        if (addressingAttempt === addressFields.length) {
          // Since we have run out of addressing strategies,
          // try the next record.
          addressingAttempt = 0;
          currentRecord += 1;
        }
        doNextGeoCode();
      }


    } else if (address !== '') {
      // Assuming we have an address to try, we should geocode it.
      nokia.places.search.manager.geoCode({
        searchTerm : address,
        onComplete: onSearchComplete
      });
    } else {
      // Otherwise we need to try another addressing strategy.
      addressingAttempt += 1;
      if (addressingAttempt === addressFields.length) {
        // Since we have run out of addressing strategies,
        // try the next record.
        addressingAttempt = 0;
        currentRecord += 1;
      }
      doNextGeoCode();
    }
  } else {
     // We can generate the KML;
    kmlGenerator.generateKML();
    // Pretty print if necessary
    if (Prism) {
      Prism.highlightAll();
    }
  }
}



function onSearchComplete(data, requestStatus) {
  if (requestStatus === 'ERROR' || data === null) {
    // Try again with geocoding the same data, using alternate fields
    // to define the address.
    $('#ticker').text('Not Found: ' + address);
    addressingAttempt += 1;
    if (addressingAttempt === addressFields.length) {
      // There are no more fall back addressing options.
      // Move on to the next record regardless.
      addressingAttempt = 0;
      currentRecord += 1;
    }
  } else {
    $('#ticker').text('Found: ' + address);
    var markerData = {};
    // Since we have an address we can add the current data to the map
    // as the addressing data has been found to be valid.
    markerData.coords = data.location.position;


    markerData.id = id;
    markerData.title = map.objects.getLength() + 1;
    markerData.description = description.trim();
    markerData.name = name.trim();
    markerData.address = address.trim();
    markerData.styleURL = styleURL.trim();
    addMarker(markerData);
    // Center on the new marker and start to process the next record.
    map.setCenter(data.location.position);
    addressingAttempt = 0;
    currentRecord += 1;
  }
  // Find the next address, either a new record or using new address fields.
  doNextGeoCode();
}

function splitAndTrimFields(source, fieldSep) {
  var fields = [],
    fieldCount,
    trimmed;

  source = source.replace('\t', '\\t').replace('\t', '\\t');
  rawFields = source.split(fieldSep);
  for (fieldCount = 0; fieldCount < rawFields.length; fieldCount += 1) {
    trimmed = rawFields[fieldCount].trim();
    fields.push(trimmed);
  }
  return fields;
}


// Decide which fields are to be placed in which part of the KML.
// Split the header from the data and process each record in turn.
function doSplit() {

  var dataInput = $('#dataInput').val(),
    from;

  headerStart = $('#headerStart').val(); //'#DEFINITION#\n' for Right Move.
  headerStart = headerStart.replace('\\n', '\n').replace('\\n', '\n'); // Convert up to two \n into carriage returns

  dataStart = $('#dataStart').val(); // '#DATA#\n' for Right Move.
  dataStart = dataStart.replace('\\n', '\n').replace('\\n', '\n'); // Convert up to two \n into carriage returns
  lineSep = $('#lineSep').val();// ; '|\n\n' for Right Move.
  lineSep = lineSep.replace('\\n', '\n').replace('\\n', '\n'); // Convert up to two \n into carriage returns


  fieldSep = $('#fieldSep').val();
  addressFields = [];
  if (($('#rGeoAttemptLat').val() !== '') && ($('#rGeoAttemptLng').val() !== '')) {
    rGeoFields = {
      latitude: $('#rGeoAttemptLat').val().split(fieldSep),
      longitude: $('#rGeoAttemptLng').val().split(fieldSep)
    };
  } else {
    rGeoFields = '';
  }


  // Each of these address strategies will be tried in turn.
  addressFields.push($('#addressAttempt1').val().split(fieldSep));
  addressFields.push($('#addressAttempt2').val().split(fieldSep));
  addressFields.push($('#addressAttempt3').val().split(fieldSep));
  addressFields.push($('#addressAttempt4').val().split(fieldSep));

  // Any fields added here will be appended to the <address> element.
  descriptionFields = splitAndTrimFields($('#descriptionFields').val(), fieldSep);
  // Any fields added here will be appended to the <name> element.
  nameFields =    $('#nameFields').val().split(fieldSep);
  // This field will be the id of the <Placemark> element.
  idFields = $('#idField').val().split(fieldSep);
  // These fields will make up the <styleURL> of the <Placemark>
  styleURLFields = $('#styleURLFields').val().split(fieldSep);
  fieldSep = fieldSep.replace('\\t', '\t').replace('\\t', '\t'); // Convert up to two \t into tabs


  if (headerStart !== '') {
     // Remove any pre-amble before the header record.
    from = dataInput.indexOf(headerStart) + headerStart.length;
    dataInput = dataInput.substr(from);
  }
  rows = dataInput.split(lineSep);
  headers = rows[0].split(fieldSep);

  if (dataStart !== '') {
    // Remove any preable prior to the first data record.
    from = dataInput.search(dataStart) + dataStart.length;
    dataInput = dataInput.substr(from);
  }

  rows = dataInput.split(lineSep);

  // Find the FIRST address. The next one will be chained from the
  // search manager.
  currentRecord = 0;
  addressingAttempt = 0;
  doNextGeoCode();
}


function geocodeButtonSetUp() {
  $('#geocodeData').click(function () {
    map.objects.clear();
    doSplit();
  });
}

function afterHereMapLoad(theMap) {
  map = theMap;
  map.set('zoomLevel', 12);
  map.set('center', [52.51, 13.4]);
  infoBubbles = new nokia.maps.map.component.InfoBubbles();
  map.components.add(infoBubbles);
  addKMLGenerator(map);
  rightMoveSetUp();
  chinaSetUp();
  finlandSetUp();
  tabSetUp();
  csvSetUp();

  geocodeButtonSetUp();

}
//}
//]]>
</script>
</body>
</html>
